
#ifndef __WEBSERVER__
#define __WEBSERVER__

#include <iostream>
#include "TcpSocket.hpp"
#include <unistd.h>
#include <thread>
#include <signal.h>
#include <functional>
#include "Entrance.hpp"
#include "ThreadPool.hpp"
#include <sys/socket.h>

// 尝试select
#include <sys/select.h>

typedef std::function<void *(const int)> function;

#define QUEUE_TASK_NUM 10 // 任务队列最大数
#define BITS 8
#define FD_NUM ((sizeof(fd_set)) * 8)
#define FD_NO -1

// 制作一个初始化服务端的类型——使用单例模式

template <class T>
class webserver
{
    typedef ThreadPool<T *> thread_pool;

private:
    int _port = -1;
    int _server_listen_socket = -1;
    thread_pool *_st = nullptr;

    int _fd_array[FD_NUM];

public:
    webserver()
    {
        signal(SIGPIPE, SIG_IGN);
        // 防止send，向关闭管道写入，导致webserver崩溃
    }

    void initial(int _port, thread_pool *th_pool)
    {
        MySocket tool;
        _server_listen_socket = tool._socket_set();
        // 设置复用端口号
        int tmp = 1;
        if (setsockopt(_server_listen_socket, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)) < 0)
            std::cout << "setsockopt fail" << std::endl;

        tool.bind_set(_server_listen_socket, _port);
        Logmessage(INFO, "服务端初始化成功");

        // 启用线程池
        _st = th_pool;
    }

    void work_begin()
    {
        MySocket::listen_run(_server_listen_socket);
        memset(_fd_array, -1, sizeof _fd_array);

        // 实现多路转接select，这里只有一个listen_socket,不过演示一下即可
        fd_set fdset;
        while (1)
        {
            timeval time = {0, 0};
            FD_ZERO(&fdset);
            FD_SET(_server_listen_socket, &fdset);
            _fd_array[0] = _server_listen_socket;
            int maxfd = _server_listen_socket;
            for (int i = 1; i < FD_NUM; i++)
            {
                if (_fd_array[i] == FD_NO)
                    continue;
                FD_SET(_fd_array[i], &fdset);
                if (_fd_array[i] > maxfd)
                    maxfd = _fd_array[i];
            }

            // 测试一下:
            for (int i = 1; i < FD_NUM; i++)
            {
                if (_fd_array[i] == FD_NO)
                    continue;
                std::cout << _fd_array[i] << " ";
            }
            std::cout << std::endl;
            // std::cout << _fd_array[0] << " " << _fd_array[1] << std::endl;

            int n = select(maxfd + 1, &fdset, nullptr, nullptr, nullptr);
            switch (n)
            {
            case 0:
                continue;
                break;

            case -1:
                Logmessage(ERROR, "select fail errno %d: %s", errno, std::to_string(errno).c_str());
                exit(-1);
                break;

            default: // 检测到读事件就绪：1. li_sten_socket 2.read, recv
                Logmessage(INFO, "select get a connect");

                int j = 1;
                for (; j < FD_NUM; j++)
                {
                    if (FD_ISSET(_fd_array[j], &fdset))
                        break;
                }

                if (j == FD_NUM)
                {
                    // 1. 读事件就绪：listen_socket
                    accepter();
                }
                else
                {
                    // 2. 读事件就绪：read,recv(文件描述符已收到资源，请尽快读取)
                    taskpush(_fd_array[j]);
                    _fd_array[j] = FD_NO;
                }
                break;
            }
        }
    }

private:
    bool accepter()
    {
        // 检测到连接
        sockaddr_in cline;
        uint16_t cline_prot;
        int actual_socket = MySocket::accept_run(_server_listen_socket, &cline, &cline_prot);
        if (actual_socket != -1)
        {
            Logmessage(INFO, "connect success 标识符:%d", actual_socket);
        }

        for (int i = 1; i < FD_NUM; i++)
        {
            if (_fd_array[i] == FD_NO)
            {
                _fd_array[i] = actual_socket;
                break;
            }
        }
        return true;
    }

    int taskpush(int socket)
    {
        // 接收到请求后，想任务池中添加任务---线程池方案
        if (_st->Get_queue_task_reserver_size() < QUEUE_TASK_NUM)
        {
            Thread_Task *task = new Thread_Task(socket);
            _st->push(task);
            _st->swap_queue(QUEUE_TASK_NUM);
        }
    }
};

#endif
